package service

import (
	"errors"
	"fmt"
	"time"
	"yunj/app/enum/articleenum"
	"yunj/app/enum/redisenum"
	"yunj/app/es/index"
	"yunj/app/model"
	"yunj/pkg/baidu"
	"yunj/pkg/global"
	"yunj/pkg/yunj/app/enum/stateenum"
	"yunj/pkg/yunj/core/log"
	"yunj/pkg/yunj/util"

	"gorm.io/gorm"
)

type article struct {
	Service
}

var Article *article

// 补充封面图，没有则补充分类图片，分类图也没有，默认图
func (a *article) ItemsAppendCover(articles []*model.Article) {
	if len(articles) == 0 {
		return
	}
	// 获取文章的分类ids
	cateIds := []uint64{}
	for _, article := range articles {
		if article.CategoryId > 0 {
			cateIds = append(cateIds, article.CategoryId)
		}
	}
	cateIds = util.SliceUnique(cateIds)
	// 获取分类图片映射
	cateImgMap := Category.GetCateImgMap(cateIds)

	for i, article := range articles {
		if article.Cover != "" {
			continue
		}
		if cateImg, exists := cateImgMap[article.CategoryId]; exists {
			articles[i].Cover = cateImg
		}
		if article.CategoryId == 0 {
			articles[i].Cover = global.Config.Default.Category.Img
		} else if cateImg, exists := cateImgMap[article.CategoryId]; exists {
			articles[i].Cover = cateImg
		} else {
			articles[i].Cover = global.Config.Default.Article.Cover
		}
	}
}

// 根据id获取数据
func (a *article) ItemById(id uint64) (*model.Article, error) {
	var article *model.Article
	err := global.MySQL.Where("id = ? and state <> ?", id, stateenum.DELETED).First(&article).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return article, nil
		} else {
			return article, fmt.Errorf("文章数据查询失败！%v", err)
		}
	}
	return article, nil
}

// 扩展属性补充
func (a *article) ItemsAttrAppend(articles []*model.Article) error {
	if len(articles) == 0 {
		return nil
	}
	// 需要重新查询attr的文章ids
	ids := []uint64{}
	for idx, article := range articles {
		if article.Attr != nil {
			article.AttrAppend()
			articles[idx] = article
		} else {
			ids = append(ids, article.Id)
		}
	}
	if len(ids) <= 0 {
		return nil
	}
	var attrs []*model.ArticleAttr
	err := global.MySQL.Where("article_id in (?)", ids).Find(&attrs).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return fmt.Errorf("文章的article_attr数据查询失败！%v", err)
	}
	for idx, article := range articles {
		if article.Attr != nil {
			continue
		}
		var attr *model.ArticleAttr
		for _, articleAttr := range attrs {
			if articleAttr.ArticleId == article.Id {
				attr = articleAttr
				break
			}
		}
		article.Attr = attr
		article.AttrAppend()
		articles[idx] = article
	}
	return nil
}

// 浏览量++
func (a *article) ViewCountInc(article *model.Article) {
	var err error
	article.ViewCount++
	err = global.MySQL.Save(&article).Error
	if err != nil {
		log.Error("文章浏览量++数据处理异常！", err, map[string]any{"article": article})
		return
	}
	// 缓存数据++
	_, err = redisenum.KeyByValue(redisenum.KEY_ARTICLE_VIEW_COUNT).IncrCache()
	if err != nil {
		log.Error("文章浏览量缓存数据自增异常！", err, map[string]any{"article": article})
	}
	// 更新ES数据
	if global.Config.Elasticsearch.Enable {
		err = index.NewArticle().DocSave(article)
		if err != nil {
			log.Error("文章浏览量++数据处理成功！ES推送异常", err, map[string]any{"article": article})
			return
		}
	}
}

// 文章新增
func (a *article) Add(article *model.Article, articleAttr *model.ArticleAttr) (err error) {
	// 数据库新增
	err = global.MySQL.Transaction(func(tx *gorm.DB) (err error) {
		dbErr := tx.Create(&article).Error
		if dbErr != nil {
			err = fmt.Errorf("文章新增异常！%v", dbErr)
			return
		}
		articleAttr.ArticleId = article.Id
		dbErr = tx.Create(&articleAttr).Error
		if dbErr != nil {
			err = fmt.Errorf("文章属性新增异常！%v", dbErr)
			return
		}
		return
	})
	article.AttrAppend(articleAttr)
	// ES新增
	if global.Config.Elasticsearch.Enable {
		esErr := index.NewArticle().DocSave(article)
		if esErr != nil {
			err = fmt.Errorf("文章新增成功！ES推送异常：%v", esErr)
			return
		}
	}
	// 推送百度收录
	if article.Status == articleenum.STATUS_PUBLISH {
		baidu.SiteMapSend([]string{a.SiteMapUrl(article.Id)})
	}
	// 清理缓存
	cacheErr := redisenum.GroupByValue(redisenum.ARTICLE_SAVE).DelCache()
	if cacheErr != nil {
		err = fmt.Errorf("文章新增成功！缓存清理异常：%v", cacheErr)
		return
	}
	return
}

// 文章更新
func (a *article) Update(article *model.Article, articleAttr *model.ArticleAttr) (err error) {
	// 数据库修改
	err = global.MySQL.Transaction(func(tx *gorm.DB) (err error) {
		dbErr := tx.Save(&article).Error
		if dbErr != nil {
			err = fmt.Errorf("文章保存异常！%v", dbErr)
			return
		}
		dbErr = tx.Save(&articleAttr).Error
		if dbErr != nil {
			err = fmt.Errorf("文章属性保存异常！%v", dbErr)
			return
		}
		return
	})
	article.AttrAppend(articleAttr)
	// ES新增
	if global.Config.Elasticsearch.Enable {
		esErr := index.NewArticle().DocSave(article)
		if esErr != nil {
			err = fmt.Errorf("文章保存成功！ES推送异常：%v", esErr)
			return
		}
	}
	// 推送百度收录
	if article.Status == articleenum.STATUS_PUBLISH {
		baidu.SiteMapSend([]string{a.SiteMapUrl(article.Id)})
	}
	// 清理缓存
	cacheErr := redisenum.GroupByValue(redisenum.ARTICLE_SAVE).DelCache()
	if cacheErr != nil {
		err = fmt.Errorf("文章保存成功！缓存清理异常：%v", cacheErr)
		return
	}
	return
}

func (a *article) SyncES(ids []uint64) (err error) {
	if !global.Config.Elasticsearch.Enable {
		return
	}
	// 如果传入ids为空，则同步所有文章（重置文章索引）
	if len(ids) == 0 {
		if esErr := index.NewArticle().IndexReset(time.Now().In(time.Local).Format("20060102150405")); esErr != nil {
			return fmt.Errorf("文章全量同步ES失败！%v", esErr)
		}
		return
	}
	var articles []*model.Article
	query := global.MySQL.Preload("Attr")
	if len(ids) > 0 {
		query = query.Where("id in ?", ids)
	}
	dbErr := query.Where("state <> ?", stateenum.DELETED).Find(&articles).Error
	if dbErr != nil {
		err = fmt.Errorf("文章同步ES数据查询失败！%v", dbErr)
		return
	}
	if len(articles) == 0 {
		return
	}
	esErr := index.NewArticle().DocBatchSave(articles)
	if esErr != nil {
		err = fmt.Errorf("文章同步ES数据保存失败！%v", esErr)
		return
	}
	return
}

// 推送百度收录
func (a *article) SendBaiduSitemap(ids []uint64) (err error) {
	if len(ids) == 0 {
		return
	}
	var articleIds []uint64
	dbErr := global.MySQL.Model(&model.Article{}).Where("id in ? and status = ? and state = ?", ids, articleenum.STATUS_PUBLISH, stateenum.NORMAL).Pluck("id", &articleIds).Error
	if dbErr != nil {
		err = fmt.Errorf("文章同步百度收录数据查询失败！%v", dbErr)
		return
	}
	if len(articleIds) == 0 {
		return
	}
	urls := make([]string, 0, len(articleIds))
	for _, articleId := range articleIds {
		urls = append(urls, a.SiteMapUrl(articleId))
	}
	baidu.SiteMapSend(urls)
	return
}

// 百度收录地址生成
func (a *article) SiteMapUrl(articleId uint64) (url string) {
	url = fmt.Sprintf("%s/article/%d.html", global.Config.App.Url, articleId)
	return
}
